<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>基础的虚拟滚动 ---- 定高--- 动态虚拟滚动</title>
    <!-- 引入 Ant Design Vue 的样式文件 -->
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/ant-design-vue/2.2.6/antd.min.css">
</head>

<body>
<div id="app">
    <div class="box" ref="box" @scroll="listScroll">
       <div class="item" v-for="(v, i) in showList" :key="i">
           {{ v.name }}
       </div>
    </div>
</div>

<!-- 引入 Vue 和 Ant Design Vue 的脚本文件 -->
<script src="../vue.js"></script>

<script>

  // 思考首先不能让list 完全渲染起来，考虑滚动的方向
  // 以向下滚动先思考，此时scrollTop 不断增大，元素定高，元素往下滚动一个元素高度，那么我就要渲染一个元素
  // list 高度是300. 一个元素高度是50， 所以一屏高度是6, 开始需要有滚动，所以初始定高最少要比一屏高一个元素
  // 所以lastIndex 最小得设置为6 + 1 , 是7 ---- 初始值

  // 通过监听滚动事件，得知用户往下面滚动了多少距离，比如用户滚动了50px 也就是一个元素的高度，我们就可以将lastIndex 设置成8
  // 那么经过computed 计算，就应该渲染1到8 这一段的数据，保证还是7个item， 渲染的元素个数没有变，完成动态渲染
  // 用户滚动了10个像素，没有一个元素高度，为了不出现空白，我们也要加载第8个元素，那么我们需要将 滚动距离/50  这个往上取整
  // 判断出错，因为我们的内部元素个数是固定的，所以只滚动一次后面就会滚动不下去了 --- 所以要在滚动到接近底部的时候重置我们的滚动距离scrollTop ，将其重置为0

  // 代表我向下的滚动空间其实只有一个元素的高度
  // 一个元素的高度为50px, 其实在滚动0-50 的时候加载下一个元素，当到达边界比如45 元素时候提前加载下一个元素，同时将元素的scrollTop 重置为0



  // 现在只考虑了向下滚动，用户也可能是向上滚动了，这就是涉及到滚动方向的判断
  // 向下scrollTop 会逐渐增大， 向上scrollTop 会变小，所以需要记录下上次scrollTop 与当前的scrollTop 进行比较

  const lastIndexInitalVal = 7
  new Vue({
    el: '#app',
    components: {},
    data() {
      return {
        lastIndex: lastIndexInitalVal,
        prevAdd: false,
        lastScrollTop: 0,
        manuStop: false,
        list: [
          { name: 1 },
          { name: 2 },
          { name: 3 },
          { name: 4 },
          { name: 5 },
          { name: 6 },
          { name: 7 },
          { name: 8 },
          { name: 9 },
          { name: 10 },
          { name: 11 },
          { name: 12 },
          { name: 13 },
          { name: 14 },
          { name: 15 },
          { name: 16 },
        ]
      };
    },
    computed: {
      // 实际展示的元素
      showList() {
        // 最小的元素编号，和0 取最大值，不可以比0 小
        const start = Math.max(this.lastIndex - lastIndexInitalVal, 0)
        const end = Math.min(this.lastIndex, this.list.length + 1)
        return this.list.slice(start, end)
      }
    },
    mounted() {

    },
    methods: {
      listScroll(e) {
        // 通过scrollTop 可以获取当前容器的滚动距离, 本质意思是被隐藏在内容区域上方的像素距离。设置该值可以改变元素的滚动位置
        // const scrollTop =  e.srcElement.scrollTop
        //  返回元素整体的高度，包括内边距，内容，边框，但是不包含外边距，包括由于 overflow 溢出而在屏幕上不可见的内容， 只读 它也包括::before和::after  这样伪元素的高度。
        // const scrollHeight = e.srcElement.scrollHeight
        const scrollTop =  e.srcElement.scrollTop
        const distance = scrollTop - this.lastScrollTop

        if (distance >= 0) { // 表示向下滑动 ---- 0 给了向下滑动，那么向上滑动就不得考虑极限值情况了
          // 已经滑动到最底部了---- 向下滑动的边界判断条件
              if (this.lastIndex === this.list.length) {
                e.srcElement.scrollTop = 50
                this.lastScrollTop = 50
                return
              }
              console.log('用户向下滚动了多少像素',  scrollTop)

              if (scrollTop >= 40) { // 当滑动距离到一个极限值的时候需要将滑动距离给重置回去
                this.lastIndex = this.lastIndex + 1
                e.srcElement.scrollTop = 0
                this.lastScrollTop = 0
                return
              }

          // 更新下上次的坐标
          this.lastScrollTop = scrollTop

        } else {

          console.log('用户向上滚动了多少像素',  scrollTop)
          if (this.lastIndex === lastIndexInitalVal) {
            e.srcElement.scrollTop = 0
            this.lastScrollTop = 0
            return
          }
          if (scrollTop < 10) {
            this.lastIndex = this.lastIndex - 1
            e.srcElement.scrollTop = 50
            this.lastScrollTop = 51
            return
          }
          this.lastScrollTop = scrollTop


        }

      }
    },
  });
</script>

<!-- 自定义的 CSS 样式 -->
<style>
    #app  {
        padding-top: 100px;
    }
    body {
        overflow: hidden;
    }
    .box {
        margin: 0 auto;
        margin-top: 150px;
        width: 200px;
        height: 300px;
        overflow: auto;
        border: 1px solid grey;
    }
    .item {
        height: 50px;
        border-bottom: 1px solid grey;
    }
</style>
</body>

</html>
